/**
 *	TrueMusic - simple Java music player
 *
 *	TrueMusic is free software; you can redistribute it
 *	and/or modify it under the terms of the GNU General
 *	Public License as published by the Free Software
 *	Foundation; either version 2 of the License or (at
 *	your option) any later version.
 *
 *	This program is distributed in the hope that it will be
 *	useful but WITHOUT ANY WARRANTY; without even the
 *	implied warranty of the MERCHANTABILITY or FITNESS FOR
 *	A PARTICULAR PURPOSE. See the GNU General Public
 *	License for more details.
 *
 *	Copyright (c)2005-2007 Ryan Macnak <rmacnak@gmail.com>
 */

package com.servequake.truemusic;

import de.jarnbjo.ogg.*;
import de.jarnbjo.vorbis.*;
import java.io.*;
import java.util.*;
import javax.sound.sampled.*;

/**
 * Audio for Ogg Vorbis using de.jarnbjo.vorbis (J-Ogg)
  */
public class OggVorbisAudio implements Audio,Runnable,SeekableAudio
{
	String res = "";
	PlaybackListener l = null;
	
	int frames = 0; //number of frames in the clip
	int pos = 0; //the next frame to play
	int current = 0; //the next frame in the decoder
	boolean pause = true;
	Thread runner;

	String title="";
	String artist="";
	String album="";
	String track="";
	String lyrics="";
	
	private static final int BUFFER_SIZE = 65536;
   
	public void load(String resource, PlaybackListener l) throws Exception
	{
		this.res = resource; this.l = l;
		
		os=new FileStream(new RandomAccessFile(res, "r"));
		los=(LogicalOggStream)os.getLogicalStreams().iterator().next();
		if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) throw new RuntimeException("Not Vorbis audio");
		vs=new VorbisStream(los);
        comments=vs.getCommentHeader();
        title = comments.getTitle(); if(title==null) title="";
        artist = comments.getArtist(); if(artist==null) artist="";
		album = comments.getAlbum(); if(album==null) album="";
		track = comments.getTrackNumber(); if(track==null) track="";
		
		
		try{while(true)
		{
			Object o = los.getNextOggPacket();
			frames++;
		}}catch(EndOfOggStreamException e){}
	}

	
	PhysicalOggStream os = null;
	LogicalOggStream los = null;

	VorbisStream vs;
	//int len=0;
	int channels;
	int sampleRate;
	CommentHeader comments;
	SourceDataLine line=null;
	FloatControl vol; 

    byte[] buffer=new byte[BUFFER_SIZE];
		 

	

	public synchronized void play()
	{
		if(pause && runner!=null) //paused
		{
			pause = false;
			notify();
			l.playbackStarted();
		}
		else if(pause && runner==null) //stopped
		{
			pause = false;
			
			try 
			{
				// open the file args[0] for reading and initialize an Ogg stream
				os=new FileStream(new RandomAccessFile(res, "r"));

				// get the first logical Ogg stream from the file
				los=(LogicalOggStream)os.getLogicalStreams().iterator().next();

				// exit, if it is not a Vorbis stream
				if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) throw new RuntimeException("Not Vorbis audio");

				// create a Vorbis stream from the logical Ogg stream
				vs=new VorbisStream(los);

				// number of data bytes written to the wav file
				//len=0;

				// get basic stream data from the vorbis channel
				channels =vs.getIdentificationHeader().getChannels();
				sampleRate=vs.getIdentificationHeader().getSampleRate();

				comments=vs.getCommentHeader();
				title = comments.getTitle(); if(title==null) title="";
		        artist = comments.getArtist(); if(artist==null) artist="";
				album = comments.getAlbum(); if(album==null) album="";
				track = comments.getTrackNumber(); if(track==null) track="";
			

				line = getOutputLine(channels, sampleRate);
			}   
			catch(IOException e) 
			{
				e.printStackTrace();
			}

			(runner = new Thread(this)).start();
			l.playbackStarted();
		}
		else if(!pause )//playing
		{
			pos = 0;
		}
	}

	public synchronized void pause()
	{
		if(pause && runner!=null) //paused
		{
			pause = false;
			notify();
			l.playbackStarted();
		}
		else if(pause && runner==null) //stopped
		{
		}
		else if(!pause )//playing
		{
			pause = true;
			l.playbackStopped();
		}
	}
	
	public synchronized void stop()
	{
		if(pause && runner!=null) //paused
		{
			runner = null;
			notify();
		}
		else if(pause && runner==null) //stopped
		{
		}
		else if(!pause )//playing
		{
			pause = true;
			runner = null;
		}
	}

	public int getFrames() {return frames;}
	public synchronized void seek(int frame) {this.pos = frame;}
	
	public void run()
	{
		try
		{
			while(runner==Thread.currentThread())
			{
				//synchronized(this)
				{
					
					while(pause) synchronized(this){wait();}
					
					if(pos<current)
					{
						os=new FileStream(new RandomAccessFile(res, "r"));
						los=(LogicalOggStream)os.getLogicalStreams().iterator().next();
						vs=new VorbisStream(los);
						current = 0;
					}
					
					while(pos>current)
					{
						//int read=vs.readPcm(buffer, 0, buffer.length);
						los.getNextOggPacket();
						current++;
					}
					
					int read=vs.readPcm(buffer, 0, buffer.length);
					current++;
		            for(int i=0; i<read; i+=2)
					{
						// swap from big endian to little endian
						byte tB=buffer[i];
						buffer[i]=buffer[i+1];
						buffer[i+1]=tB;
					}
	              
					//len+=read;
				  
					if(vol!=null)vol.setValue(l.getVolume());
					
					line.write(buffer,0,read);
					pos++;
					l.frameCompleted(pos);
				}		
            }
        }
        catch(EndOfOggStreamException e)
		{
			l.playbackCompleted();
        }
		catch(Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			pos = 0;
			pause = true;
			runner = null;
			l.frameCompleted(0);
		}
	}
	
	
	SourceDataLine getOutputLine(int channels, int rate)
	{
		try
		{
			AudioFormat af = new AudioFormat( (float)rate, 16, channels, true, false );
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, af, AudioSystem.NOT_SPECIFIED);

			SourceDataLine outputLine = (SourceDataLine) AudioSystem.getLine(info);
			outputLine.open(af);
			outputLine.start();
			
			vol = (FloatControl)outputLine.getControl(FloatControl.Type.MASTER_GAIN);
			return outputLine;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
	
	public boolean isPlaying(){return !pause;}

	public String getResource(){return res;}
	public String getTitle(){if(title=="") return new File(res).getName(); return title;}
	public String getArtist(){return artist;}
	public String getAlbum(){return album;}
	public String getTrack(){return track;}
	public String getLyrics(){return lyrics;}
	public String getGenre(){return "";}
	public String toString()
	{if(title=="") return new File(res).getName(); return "\""+title+"\" by "+artist;}

	public String formatName(){return "Ogg Vorbis";}
	public String[] getExtensions(){return new String[]{".ogg"};}	
}
